import { ReceptionContext } from '@/components/DataPreview/task';
import { kernel, model, schema } from '@/ts/base';
import { NodeType } from '@/ts/base/enum';
import { IBelong } from '@/ts/core';
import { FormChangeEvent } from '@/ts/scripting/core/types/rule';
import { HotTable } from '@handsontable/react';
import Handsontable from 'handsontable';
import 'handsontable/dist/handsontable.min.css';
import { registerLanguageDictionary, zhCN } from 'handsontable/i18n';
import { registerAllModules } from 'handsontable/registry';
import { textRenderer } from 'handsontable/renderers';
import { CellSettings } from 'handsontable/settings';
import React, {
  ReactNode,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import { getWidget, isDirectEditable } from '../../../DataStandard/WorkForm/Utils';
import { CellInfo, ReportInfo, ReportSettingInfo, stagDataInfo } from '../types';
import CellItem from './cellItem';
import WorkFormService from '@/ts/scripting/core/services/WorkFormService';
import Toolbar, { Item } from 'devextreme-react/toolbar';
import { EditModal } from '@/executor/tools/editModal';
import { XAttribute, XThing } from '@/ts/base/schema';
import { CellChange, ChangeSource } from 'handsontable/common';
import { ReportReception } from '@/ts/core/work/assign/reception/report';
import { useFixedCallback } from '@/hooks/useFixedCallback';
import { ReportStatus } from '@/ts/base/model';
import { Modal, Table, Tag, message } from 'antd';
import { formatNumber } from '@/utils';
registerLanguageDictionary(zhCN);
registerAllModules();

const WorkReportViewer: React.FC<{
  data: XThing;
  allowEdit: boolean;
  belong: IBelong;
  form: schema.XForm;
  info?: model.FormInfo;
  readonly?: boolean;
  showTitle?: boolean;
  fields: model.FieldModel[];
  rules: model.RenderRule[];
  formData?: model.FormEditData;
  onValuesChange?: (fieldId: string, value: any, data: any, coord?: any) => void;
  activeTabKey?: string;
  height?: string;
  service?: WorkFormService;
}> = (props) => {
  props.data.name = props.form.name;
  const [editMode, setEditMode] = useState<boolean>(false);
  const [field, setField] = useState<model.FieldModel>(props.fields[0]);
  const [coordinate, setCoordinate] = useState<any>();
  const [stagData, setStagData] = useState<stagDataInfo[]>([]);
  const [selectValue, setSelectValue] = useState<any>();
  const [cells, setCells] = useState<CellInfo[]>([]);
  const hotRef = useRef<{ hotInstance: Handsontable }>(null!);

  const [ready, setReady] = useState(false);

  const [currentCell, setCurrentCell] = useState<CellInfo | null>(null);

  const attrMap = useMemo(() => {
    return props.form.attributes.reduce<Dictionary<XAttribute>>((a, v) => {
      a[v.id] = v;
      return a;
    }, {});
  }, [props.form.attributes]);
  const fieldMap = useMemo(() => {
    return props.fields.reduce<Dictionary<model.FieldModel>>((a, v) => {
      a[v.id] = v;
      return a;
    }, {});
  }, [props.fields]);

  const reception = useContext(ReceptionContext) as ReportReception | null;
  const reportStatus = useMemo(() => {
    if (!reception) {
      return null;
    }
    return reception.metadata.content as ReportStatus;
  }, [reception]);

  const isSummary = reportStatus?.treeNode.nodeType == NodeType.Summary;

  const onValueChange = (fieldId: string, value: any) => {
    if (coordinate?.row) {
      setStagData([
        ...stagData,
        { row: coordinate.row, col: coordinate.col, fieldId: fieldId, value: value },
      ]);
    }
    const checkHasChanged = (fieldId: string, value: any) => {
      if (value instanceof Object) {
        return true;
      }
      const oldValue = props.data[fieldId];
      if (oldValue) {
        return value != oldValue || value === undefined || value === null;
      } else {
        return value !== undefined && value !== null;
      }
    };
    if (checkHasChanged(fieldId, value)) {
      if (value === undefined || value === null) {
        delete props.data[fieldId];
      } else {
        props.data[fieldId] = value;
      }
      props.onValuesChange?.apply(this, [fieldId, value, props.data]);
    }
  };
  const writeData = (text: string) => {
    const hot = hotRef.current.hotInstance;
    hot.setDataAtCell(coordinate.row, coordinate.col, text, 'writeData');
    const coordinateId = [coordinate.row, coordinate.col].toString();
    props.onValuesChange?.apply(this, [coordinateId, text, props.data]);
  };

  function isAllDigits(str: string) {
    return /^\d+$/.test(str);
  }

  async function refreshFormData(cellList: CellInfo[]) {
    const data: [number, number, any][] = [];
    if (props.formData?.after) {
      for (let key in props.formData.after[0]) {
        for (let item of cellList) {
          let value = props.formData?.after[0][key];
          let pass = false;
          if (key === item.prop?.propId) {
            pass = true;
          } else if (key.includes(',')) {
            const coords = key.split(',').map((v) => parseInt(v));
            if (coords[0] == item.row && coords[1] == item.col) {
              pass = true;
            }
          }
          if (pass) {
            const prop = attrMap[item.prop.id];
            switch (getWidget(prop.valueType, prop.widget)) {
              case '操作人':
              case '操作组织':
                if (value) {
                  if (isAllDigits(value)) {
                    let result = await kernel.queryEntityById({ id: value });
                    if (result.success && result.data) {
                      value = result.data.name;
                    }
                  }
                }
                break;
            }
            data.push([item.row, item.col, value]);
          }
        }
      }
    }
    return data;
  }

  const onBatchUpdate = useFixedCallback(async (changeEvents: FormChangeEvent[]) => {
    const hot = hotRef.current.hotInstance;
    let changeArray: [number, number, any][] = [];
    for (const item of changeEvents) {
      let change: [number, number, any] | null = null;
      let needText = false;

      let prop = cells.find((value) => {
        return value.prop.id === item.destId;
      });
      if (!prop) {
        continue;
      }

      if (item.value || item.value == 0) {
        const field = fieldMap[prop.prop.id];
        switch (field?.valueType) {
          case '选择型':
          case '分类型': {
            const value = field.lookups?.find((lookup) => lookup.value === item.value);
            change = [prop.row, prop.col, value?.text];
            needText = true;
            break;
          }
          case '用户型':
            switch (field.widget) {
              case '操作人':
              case '操作组织':
                // 自动生成的，不赋值
                continue;
              case '人员搜索框':
              case '单位搜索框':
              case '群组搜索框':
              case '组织群搜索框':
              case '成员选择框':
              case '内部机构选择框': {
                const result = await kernel.queryEntityById({ id: item.value });
                change = [prop.row, prop.col, result.data?.name];
                needText = true;
                break;
              }
            }
            break;
          default:
            change = [prop.row, prop.col, item.value];
            break;
        }
      }

      // 给表单赋值
      props.data[item.destId] = item.value;
      // 居然没有地方通知更新，依赖Bug运行？？？
      onValueChange(item.destId, item.value);

      if (change) {
        changeArray.push(change);
        if (needText) {
          const coordinateId = [prop.row, prop.col].join(',');
          props.data[coordinateId] = change[2];
        }
      }
    }
    // 更新handsontable并阻止afterChange事件
    hot.setDataAtCell(changeArray, 'writeData');
  });

  useEffect(() => {
    let handles: (() => void)[] = [];
    if (props.service) {
      handles = (['afterCalculate', 'batchUpdate'] as const).map((type) =>
        props.service!.onScoped(type, onBatchUpdate),
      );
    }

    const sheetListData: any = JSON.parse(props.form?.reportDatas);
    const selectItem = Object.values(sheetListData)[0] as ReportInfo;
    const setting = selectItem?.data?.setting || {};
    const datas = selectItem?.data?.data || [[]];
    updateHot(setting, datas);

    return () => {
      handles.forEach((dispose) => dispose());
    };
  }, []);

  async function onCellsChange() {
    const hot = hotRef.current.hotInstance;
    const changes = await refreshFormData(cells);
    hot.setDataAtCell(changes);
  }

  useEffect(() => {
    if (!ready) {
      return;
    }
    onCellsChange();
  }, [cells]);

  const updateHot = async (setting: ReportSettingInfo, data: any[][]) => {
    const hot = hotRef.current.hotInstance;
    const cells: CellSettings[] = [];

    function getCellSetting(row: number, col: number) {
      let cell = cells.find((c) => c.col == col && c.row == row);
      if (!cell) {
        cell = { col, row };
        cells.push(cell);
      }
      return cell;
    }

    function setData(row: number, col: number, value: any) {
      if (!data[row]) {
        data[row] = [];
      }
      data[row][col] = value;
    }

    const styleList: any[] = setting.styleList || [];
    const classList: any[] = setting.classList || [];

    styleList.forEach((item: any) => {
      const cell = getCellSetting(item.row, item.col);
      cell.renderer = (
        instance: Handsontable.Core,
        TD: HTMLTableCellElement,
        row: number,
        col: number,
        prop: string | number,
        value: any,
        cellProperties: Handsontable.CellProperties,
      ) => {
        textRenderer(instance, TD, row, col, prop, value, cellProperties);
        const items = setting.styleList.find(
          (it: any) => it.row === row && it.col === col,
        );
        if (items) {
          for (let key in items.styles) {
            if (key === 'paddingLeft') {
              TD.style[key] = items.styles[key] + 'px';
            } else {
              TD.style[key as any] = items.styles[key];
            }
          }
        }
      };
    });
    classList.forEach((item: any) => {
      const cell = getCellSetting(item.row, item.col);
      let arr = [];
      for (let k in item.class) {
        arr.push(item.class[k]);
      }
      cell.className = arr.join(' ');
    });

    setting.cells?.forEach((item: CellInfo) => {
      const cell = getCellSetting(item.row, item.col);
      const prop = attrMap[item.prop.id];

      if (!props.readonly) {
        cell.readOnly = prop.options?.readOnly;
      }
      cell.renderer = 'customStylesRenderer';
      if ((prop.rule && JSON.parse(prop.rule).id) || prop.options?.isComputed) {
        cell.renderType = 'computed';
      } else {
        cell.renderType = 'input';
      }
      switch (getWidget(prop.valueType, prop.widget)) {
        case '数字框':
          cell.type = 'numeric';
          cell.numericFormat = {
            pattern: {
              // 传undefined也会报错，逆天
              // mantissa: prop.options?.accuracy,
              thousandSeparated: true,
            },
            culture: 'zh-CN',
          };
          if (typeof prop.options?.accuracy === 'number') {
            (cell.numericFormat.pattern as any).mantissa = prop.options?.accuracy;
          }
      }
      switch (prop.valueType) {
        case '选择型':
        case '分类型':
        case '用户型':
          cell.validator = function (text, callback) {
            const value = props.data[prop.id];
            if (!value && !text) {
              return callback(true);
            } else if (!value && text) {
              if (prop.widget == '操作人' || prop.widget == '操作组织') {
                return callback(true);
              }
              // 没有值但有中文，说明是手填的脏数据
              return callback(false);
            }
            if (typeof value !== 'string') {
              return callback(false);
            }
            if (['选择型', '分类型'].includes(prop.valueType!) && /^S\d+$/.test(value)) {
              return callback(true);
            } else if (prop.valueType == '用户型' && /^\d+$/.test(value)) {
              return callback(true);
            }
            // 手填的脏数据
            return callback(false);
          };
          break;
        default:
          break;
      }
      props.fields.forEach((field) => {
        if (prop.id === field.id) {
          if (field.options!.readOnly && props.readonly) {
            cell.readOnly = true;
          }
          if (field.options!.defaultValue) {
            if (field.lookups!.length > 0) {
              const items = field.lookups!.find(
                (it: any) => it.value === field.options!.defaultValue,
              );
              setData(item.row, item.col, items?.text);
            } else {
              setData(item.row, item.col, field.options!.defaultValue);
            }
          }

          if (isSummary && field.options?.isSummary) {
            cell.readOnly = true;
            cell.className = 'is-readonly';
          }
        }
      });

      cells.push(cell);
    });
    setCells(setting?.cells || []);

    hot.updateSettings({
      // cell: cells,
      data,
      minCols: setting.col_w.length,
      minRows: setting.row_h.length,
      mergeCells: setting.mergeCells || [],
      rowHeights: setting.row_h,
      colWidths: setting.col_w,
    });

    const changes = await refreshFormData(setting?.cells || []);
    // 这种写法会报错，但下面的不会？？
    // hot.setDataAtCell(changes);
    hot.batch(() => {
      for (const change of changes) {
        hot.setDataAtCell(...change);
      }
    });

    hot.updateSettings({
      cell: cells,
    });

    setTimeout(() => {
      setReady(true);
    }, 20);
  };

  const afterOnCellMouseDown = (_event: MouseEvent, coords: Handsontable.CellCoords) => {
    if (!props.readonly) {
      cells.forEach((item: CellInfo) => {
        const prop = attrMap[item.prop.id];
        if (item.col === coords.col && item.row === coords.row) {
          switch (getWidget(prop.valueType, prop.widget)) {
            case '选择框':
            case '单选框':
            case '引用选择框':
            case '多级选择框':
            case '人员搜索框':
            case '单位搜索框':
            case '群组搜索框':
            case '组织群搜索框':
            case '成员选择框':
            case '内部机构选择框':
            case '日期选择框':
            case '时间选择框':
              setCoordinate({ col: item.col, row: item.row });
              setEditMode(true);
              setSelectValue(undefined);
              stagData.forEach((items: stagDataInfo) => {
                if (items.col === item.col && items.row === item.row) {
                  setSelectValue(items.value);
                }
              });
              props.fields.map((it) => {
                if (it.id == prop.id) {
                  setField(it);
                }
              });
              break;
          }
        }
      });
    }
  };

  function afterSelection(row: number, col: number, row2: number, col2: number) {
    const cell = cells.find((c) => c.col == col && c.row == row);
    if (cell) {
      setCurrentCell(cell);
    } else {
      setCurrentCell(null);
    }
  }

  const afterChange = (changes: CellChange[] | null, source: ChangeSource) => {
    if (!ready) {
      return;
    }
    if (
      (source === 'edit' || source === 'CopyPaste.paste' || source === 'Autofill.fill') &&
      !props.readonly
    ) {
      const hot = hotRef.current.hotInstance;
      changes?.forEach((change: CellChange) => {
        var row = change[0];
        var col = change[1];
        var newValue = change[3];
        for (var i = 0; i < cells.length; i++) {
          const cell = cells[i];
          if (cell.row == row && cell.col == col) {
            const prop = attrMap[cell.prop.id];

            let canChange = false;
            if (isDirectEditable(prop) || source === 'edit') {
              // 错误把数字格式化成了字符串，而且FormService已经处理过了
              // if (prop.options?.accuracy && prop.widget === '数字框') {
              //   if (newValue != null) {
              //     newValue = Number(newValue).toFixed(Number(prop.options?.accuracy));
              //   }
              // }
              if (prop.widget == '数字框' && typeof newValue === 'string') {
                newValue = parseFloat(newValue);
                if (!isNaN(newValue)) {
                  canChange = true;
                }
              } else {
                canChange = true;
              }
            }

            if (canChange) {
              onValueChange(prop.propId, newValue);
            } else {
              // 阻止粘贴和自动填充分类型与用户型，还原值
              const key = `${row},${col}`;
              hot.setDataAtCell(row, col as number, props.formData?.after[0]?.[key]);
            }
            return;
          }
        }
      });
    }
  };

  async function propSummaryTree() {
    if (!currentCell) {
      message.warning('请先选择有属性的单元格');
      return;
    }
    const attr = attrMap[currentCell.prop.id];
    if (!attr?.options?.isSummary) {
      message.warning('当前属性不可汇总');
      return;
    }

    if (attr.widget != '数字框') {
      message.warning('请选择数值型的属性');
      return;
    }

    let res: model.ReportSummaryTreeNodeView;
    try {
      res = await reception!.propertySummaryTree(attr, props.form);
      console.log(res);
    } catch (error) {
      console.error(error);
      message.error(error instanceof Error ? error.message : String(error));
      return;
    }

    Modal.info({
      width: 800,
      title: '属性值汇总穿透',
      content: (
        <div style={{ height: '60vh' }}>
          <Table
            size="small"
            bordered
            pagination={false}
            scroll={{ y: '50vh' }}
            columns={[
              {
                title: '节点名称',
                dataIndex: 'name',
                key: 'name',
              },
              {
                title: '节点类型',
                dataIndex: 'nodeTypeName',
                key: 'nodeTypeName',
                width: 120,
                render: (value) => <Tag color="success">{value}</Tag>,
              },
              {
                title: attr.name,
                dataIndex: 'value',
                key: 'value',
                width: 200,
                align: 'right',
                render: (value) => {
                  if (value == null) {
                    return '';
                  }
                  return formatNumber(value, attr.options?.accuracy ?? null, true);
                },
              },
            ]}
            dataSource={[res]}
          />
        </div>
      ),
    });
  }

  const buttons: ReactNode[] = [];
  if (props.allowEdit && props.info?.allowSelect) {
    buttons.push(
      <Item
        location="after"
        locateInMenu="never"
        widget="dxButton"
        visible
        options={{
          text: '数据选择',
          type: 'default',
          stylingMode: 'outlined',
          onClick: () => {
            EditModal.showFormSelect({
              form: props.form,
              fields: props.fields,
              belong: props.belong,
              multiple: false,
              onSave: (values) => {
                if (values.length > 0) {
                  const form = values[0];
                  onBatchUpdate(
                    props.fields.map((f) => ({
                      formId: props.form.id,
                      destId: f.id,
                      value: form[f.id],
                    })),
                  );
                }
              },
            });
          },
        }}
      />,
    );
  }
  if (isSummary) {
    buttons.push(
      <Item
        location="after"
        locateInMenu="never"
        widget="dxButton"
        visible
        options={{
          text: '属性值穿透',
          type: 'default',
          stylingMode: 'outlined',
          onClick: propSummaryTree,
        }}
      />,
    );
  }

  return (
    <div className="report-form-viewer">
      <Toolbar height={60} visible={buttons.length > 0}>
        {buttons}
      </Toolbar>
      <HotTable
        ref={hotRef as any}
        readOnly={true}
        customBorders={true}
        rowHeaders={true}
        colHeaders={true}
        manualColumnResize={true}
        manualRowResize={true}
        height={props?.height ?? '100%'}
        language={zhCN.languageCode}
        persistentState={true}
        stretchH="all"
        outsideClickDeselects={false}
        renderAllRows={true}
        afterChange={afterChange}
        licenseKey="non-commercial-and-evaluation"
        afterOnCellMouseDown={afterOnCellMouseDown}
        afterSelection={afterSelection}
      />
      {editMode && (
        <CellItem
          data={props.data}
          belong={props.belong}
          rules={[...(props.formData?.rules ?? []), ...(props?.rules ?? [])].filter(
            (a) => a.destId == field.id,
          )}
          readonly={props.readonly}
          field={field}
          onValuesChange={onValueChange}
          writeData={writeData}
          selectValue={selectValue}
          onCancel={() => {
            setEditMode(false);
          }}
        />
      )}
    </div>
  );
};

export default WorkReportViewer;
